#include "../udp.h"
#include "../uv_common.h"
#include "../uv_thread.h"
#include <stdio.h>
#include <errno.h>

#pragma comment(lib,"ws2_32.lib")
int g_recv_count = 0;
int g_send_count = 0;
int g_sendcb_count = 0;

    typedef int32_t ssize_t ;


    struct RecvReq{
        REQCOMMON
        uv_recv_cb cb;

        struct sockaddr fromAddr;
        int fromAddrLen;

        char *buf; //store recv data
        ULONG buflen;
    };
    

HANDLE g_completionPort = NULL;

void UWorkerThread(LPVOID ComPortID)
{
    HANDLE cp = (HANDLE)ComPortID;
    BOOL ret = 0;
    while (1)
    {
        //Returns nonzero (TRUE) if successful or zero (FALSE)otherwise
        DWORD nread = 0;
        ReqCommon_t *overlapUserParm = NULL;
        udp_t *userparm = NULL;
        if (GetQueuedCompletionStatus(cp, &nread, (LPDWORD)&userparm, (LPOVERLAPPED *)&overlapUserParm, WSA_INFINITE) == 0){
            ret = GetLastError();
            printf("GetQueuedCompletionStatus error:%d!\n", ret);
        }

        //
        if(overlapUserParm)
        {
            switch (overlapUserParm->optype) {
            case RECV:
            {
                RecvReq_t *recvreq = (RecvReq_t *)overlapUserParm;
				uv_buf_t buf = { recvreq->buflen, recvreq->buf };
                if(nread > 0)
                    printf("%s:RECVcb %d\n", __func__, ++g_recv_count);

                if (recvreq->cb) {
                    recvreq->cb(recvreq->handle,
                        nread, &buf, &recvreq->fromAddr, ret);
                }
                udp_recv_start(recvreq->handle, recvreq->cb);
                free(recvreq);
                break;
            }
            case SEND: {
                
                SendReq_t *sendreq = (SendReq_t *)overlapUserParm;
				//printf("%s:SENDcb %d\n", __func__, ++g_sendcb_count);
                if (sendreq->cb) {
                    sendreq->cb(sendreq, ret);
                }
                break;
            }
            case SHUTDOWN: {
                ShutdownReq_t *shutdownreq = (ShutdownReq_t *)overlapUserParm;
                printf("%s:SHUTDOWN\n", __func__);

                if (shutdownreq->cb) {
                    shutdownreq->cb(shutdownreq, ret);
                }
                break;
            }
            default: {
                printf("%s:unknown optype\n", __func__);
            }
            }
            
        } else {
            printf("%s overlapped parm is NULL\n", __func__);
        }
    }

}

int udp_init()
{
    int ret = 0;
    /*
	*
	*/
    if (g_completionPort == NULL) {
        WSADATA wsaData;
        ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
        printf("%s\n", __func__);

        g_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
        if(g_completionPort == INVALID_HANDLE_VALUE){
            ret = GetLastError();
            printf("%s failed %d", ret);
        }else{
            uv_thread_t tid;
            uv_thread_create(&tid, UWorkerThread, g_completionPort);
        }
    }

    return ret;
}

int udp_createhandler(udp_t *udphandle, struct sockaddr *addr, size_t addrlen)
{
    int ret = 0;
	DWORD yes = 1;
	HANDLE h ;
    //udphandle = (udp_t*)calloc(1, sizeof(udp_t));
    udphandle->socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    /* Set the socket to nonblocking mode */
    if (ioctlsocket(udphandle->socket, FIONBIO, &yes) == SOCKET_ERROR) {
        ret = WSAGetLastError();
    }
#if 0
    int ttl = 128;
    setsockopt(udphandle->socket, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));
#endif
    if (addr == NULL || addrlen <= 0) {
        uv_ip4_addr("0.0.0.0", 0, (struct sockaddr_in*)&udphandle->addr);
        addrlen = sizeof(udphandle->addr);
    } else {
        memcpy(&udphandle->addr, addr, addrlen);
    }
    
    ret = bind(udphandle->socket, (SOCKADDR *)&udphandle->addr, addrlen);
    if (ret)
    {
        ret = WSAGetLastError();
        printf("bind failed %d\n", ret);
        return ret;
    }
    
    udp_getsockname(udphandle, &udphandle->addr, &addrlen);

    h = CreateIoCompletionPort((HANDLE)udphandle->socket, g_completionPort,
                                      (DWORD)udphandle, 0);

    return ret;
}

#define INITCOMMONREQ(type) \
if(req){ \
    memset(&req->ol, 0, sizeof(req->ol)); \
    req->optype = type; \
    req->cb = cb; \
    req->handle = udphandle; \
}

void udp_recv_start(udp_t *udphandle, uv_recv_cb cb)
{
    int ret = 0;
	WSABUF buf;
	DWORD dRecv = 0;
	DWORD dFlag = 0;

    //begin recv

    RecvReq_t *req = (RecvReq_t *)calloc(1, sizeof(RecvReq_t));
    INITCOMMONREQ(RECV)

    
    req->buf = buf.buf = (char *)malloc(RECVBUFLEN);
    req->buflen = buf.len = RECVBUFLEN;

    req->fromAddrLen = sizeof(req->fromAddr);

    ret = WSARecvFrom(udphandle->socket, &buf, 1, &dRecv, &dFlag,
                      (struct sockaddr *)&req->fromAddr,
                      &req->fromAddrLen,
                      &req->ol, NULL);
    if (ret)
    {
        ret = WSAGetLastError();
        if (ret != ERROR_IO_PENDING)
        {
            printf("WSARecvFrom failed = %d\n", ret);
        }
    }
}

int udp_getsockname(const udp_t* handle, struct sockaddr* name, int* namelen) {
    int result;

    if (handle->socket == INVALID_SOCKET) {
        return EINVAL;
    }

    result = getsockname(handle->socket, name, namelen);
    if (result != 0) {
        return WSAGetLastError();
    }

    return 0;
}


void udp_send(SendReq_t *req, udp_t *udphandle, const char *buf, size_t len, const struct sockaddr *addr, uv_send_cb cb)
{
	int ret = 0;
	WSABUF bufs;
	int addrlen;
    //printf("udp send=%d\n", ++g_send_count);
    INITCOMMONREQ(SEND)

    bufs.buf = (char *)buf;
    bufs.len = len;

    addrlen = sizeof(struct sockaddr_in);
    //
    ret = WSASendTo(udphandle->socket, &bufs, 1, NULL, 0, addr, addrlen, &req->ol, NULL);
    if (ret) {
        ret = WSAGetLastError();
        if (ret != ERROR_IO_PENDING) {
            printf("%s:ret=%d\n", __func__, ret);
        }
    }
}

void udp_shutdown(ReqCommon_t *req, udp_t *udphandle, uv_shutdown_cb cb)
{
	int ret = 0;
	char strerr[100] = {0};
    INITCOMMONREQ(SHUTDOWN)

    ret = shutdown(udphandle->socket, SD_BOTH);
	if (ret == SOCKET_ERROR )
	{
		ret = WSAGetLastError();
		sprintf(strerr,"udp_shutdown = %d\r\n",ret);
		OutputDebugString(strerr);
	}
}

int udp_close(udp_t *udphandle)
{
	int ret = 0;
	char err[100] = {0};
    if (closesocket(udphandle->socket)) {
        ret = WSAGetLastError();
		
		sprintf(err,"udp_close = %d\r\n",ret);
		OutputDebugString(err);
		return ret;
    }
    return 0;
}










////////////////////////test////////////////////////
